import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Like, In } from 'typeorm';
import { Text } from './entities/text.entity';
import { Tag } from '../tags/entities/tag.entity';
import { TextTag } from './entities/text-tag.entity';
import { CreateTextDto } from './dto/create-text.dto';
import { UpdateTextDto } from './dto/update-text.dto';
import { TextQueryDto } from './dto/text-query.dto';

@Injectable()
export class TextsService {
  constructor(
    @InjectRepository(Text)
    private readonly textRepository: Repository<Text>,
    @InjectRepository(Tag)
    private readonly tagRepository: Repository<Tag>,
    @InjectRepository(TextTag)
    private readonly textTagRepository: Repository<TextTag>,
  ) {}

  async findAll(query: TextQueryDto, userId: number) {
    const { keyword, tagId, page = 1, pageSize = 20, limit } = query;
    const actualPageSize = limit || pageSize;
    const skip = (page - 1) * actualPageSize;

    let queryBuilder = this.textRepository
      .createQueryBuilder('text')
      .leftJoinAndSelect('text.textTags', 'textTag')
      .leftJoinAndSelect('textTag.tag', 'tag')
      .where('text.userId = :userId', { userId })
      .orderBy('text.createdAt', 'DESC')
      .skip(skip)
      .take(actualPageSize);

    if (keyword) {
      queryBuilder = queryBuilder.andWhere(
        '(text.title LIKE :keyword OR text.content LIKE :keyword)',
        { keyword: `%${keyword}%` },
      );
    }

    if (tagId) {
      queryBuilder = queryBuilder.andWhere('tag.id = :tagId', { tagId });
    }

    const [texts, total] = await queryBuilder.getManyAndCount();

    const list = texts.map(text => ({
      ...text,
      tags: text.textTags?.map(textTag => textTag.tag) || [],
    }));

    const totalPages = Math.ceil(total / actualPageSize);

    return {
      list,
      pagination: {
        page,
        pageSize: actualPageSize,
        total,
        totalPages,
        hasMore: page < totalPages,
      },
    };
  }

  async findOne(id: number, userId: number) {
    const text = await this.textRepository.findOne({
      where: { id, userId },
      relations: ['textTags', 'textTags.tag'],
    });

    if (!text) {
      throw new NotFoundException('文字不存在');
    }

    return {
      ...text,
      tags: text.textTags?.map(textTag => textTag.tag) || [],
    };
  }

  async create(createTextDto: CreateTextDto, userId: number) {
    const text = this.textRepository.create({
      ...createTextDto,
      userId,
    });
    const savedText = await this.textRepository.save(text);

    if (createTextDto.tagIds && createTextDto.tagIds.length > 0) {
      await this.updateTextTags(savedText.id, createTextDto.tagIds, userId);
    }

    return await this.findOne(savedText.id, userId);
  }

  async update(id: number, updateTextDto: UpdateTextDto, userId: number) {
    const text = await this.textRepository.findOne({ where: { id, userId } });

    if (!text) {
      throw new NotFoundException('文字不存在');
    }

    // 从 DTO 中分离出 tagIds，因为 Text 实体中没有这个字段
    const { tagIds, ...textData } = updateTextDto;

    // 只更新 Text 实体的字段（title, content）
    if (Object.keys(textData).length > 0) {
      await this.textRepository.update(id, textData);
    }

    // 单独处理标签关联
    if (tagIds !== undefined) {
      await this.updateTextTags(id, tagIds, userId);
    }

    return await this.findOne(id, userId);
  }

  async remove(id: number, userId: number) {
    const text = await this.textRepository.findOne({ where: { id, userId } });

    if (!text) {
      throw new NotFoundException('文字不存在');
    }

    await this.textRepository.remove(text);
  }

  private async updateTextTags(textId: number, tagIds: number[], userId: number) {
    await this.textTagRepository.delete({ text: { id: textId } });

    if (tagIds.length > 0) {
      const tags = await this.tagRepository.findBy({ id: In(tagIds), userId });
      const textTags = tags.map(tag =>
        this.textTagRepository.create({
          text: { id: textId },
          tag: { id: tag.id },
        }),
      );

      await this.textTagRepository.save(textTags);
    }
  }
}